# -*- coding: utf-8 -*-

import hashlib
from datetime import datetime
import time

from google.appengine.ext import db
from google.appengine.api import datastore
from google.appengine.api import datastore_errors
from google.appengine.api import users


BadValueError = datastore_errors.BadValueError
ValidationError = BadValueError

#@staticmethod
#@classmethod

def __typecheck__(obj, type):
    if not isinstance(obj,type):
        raise TypeError('Expected %s type; received %s (is %s)' %
                        (type, obj, obj.__class__.__name__))

class XMSUser(db.Model):
    JoinDate = db.DateTimeProperty(auto_now_add=True)
    User = db.UserProperty()
    NickName = db.StringProperty()
    Intro = db.StringProperty(multiline=True)
    Actived = db.BooleanProperty(default=True)
    Language = db.StringProperty(default="en-us", choices = ("en-us","zh-cn"))
    
    def __str__(self):
        return self.NickName.encode('utf-8')
    
    def __unicode__(self):
        return self.NickName
    
    def __cmp__(self, other):
        if not isinstance(other, XMSUser):
            return NotImplemented
        return cmp((self.User, self.NickName),
               (other.User, other.NickName))
    
    @staticmethod
    def GetXMSUserByUser(user):
        q = db.GqlQuery("SELECT * FROM XMSUser Where User=:1", user)
        result = q.fetch(1)
        return result and result[0]
    
    @staticmethod
    def GetXMSUserByNickName(nickname):
        q = db.GqlQuery("SELECT * FROM XMSUser Where NickName=:1", nickname)
        result = q.fetch(1)
        return result and result[0]
    
    @staticmethod
    def CheckUserExist(nickname = None, user = None):
        if nickname:
            for xmsuser in XMSUser.all():
                if xmsuser.NickName.lower() == nickname.lower():
                    return True
        if user and XMSUser.GetXMSUserByUser(user):
            return True
        return False
    
    def JoinGroup(self, group):
        __typecheck__(group, XMSGroup)
        if self.IsUserOfGroup(group):
            raise ValidationError('User %s already in Group %s' % (self.NickName,group.GroupName) )
        if group.Actived == False:
            raise ValidationError('Can not join inactive group %s' % (group.GroupName) )
        if self.User not in group.AWaitingJoinUser:
            group.AWaitingJoinUser.append(self.User)
            group.put()
        else:
            raise ValidationError('User %s already in Application' % (self.NickName) )
        #gu = XMSGroupAndUser(User = self, Group = group)
        #gu.put()
    
    def IsUserOfGroup(self, group):
        __typecheck__(group, XMSGroup)
        if self.IsOwnerOfGroup(group) or self.IsAdminOfGroup(group):
            return True    
        
        gkey = group.key()
        for ug in self.UserGroups:
            if gkey == ug.Group.key():
                return True
        
        return False
    
    
    def IsAdminOfGroup(self, group):
        __typecheck__(group, XMSGroup)
        gkey = group.key()
        for g in self.OwnGroups:
            if gkey == g.key():
                return True
        for ug in self.AdminGroups:
            if gkey == ug.Group.key():
                return True
        return False
               
    def IsOwnerOfGroup(self, group):
        __typecheck__(group, XMSGroup)
        gkey = group.key()
        for g in self.OwnGroups:
            if gkey == g.key():
                return True
        return False
               
    def GetAllGroups(self, actived = True):
        keys = set([ug.Group.key() for ug in self.UserGroups if ug.Group.Actived == actived]\
                    + [ug.Group.key() for ug in self.AdminGroups if ug.Group.Actived == actived]\
                    + [g.key() for g in self.OwnGroups if g.Actived == actived])
        return XMSGroup.get(keys)  
    
    def GetAllGroupsQuery(self, actived = True):
        groupnamelist = set([ug.Group.GroupName for ug in self.UserGroups if ug.Group.Actived == actived]\
                    + [ug.Group.GroupName for ug in self.AdminGroups if ug.Group.Actived == actived]\
                    + [g.GroupName for g in self.OwnGroups if g.Actived == actived])
        
        groupnamelist = list(groupnamelist)
        if not groupnamelist:
            groupnamelist = ["<"]
        q = XMSGroup.gql("Where GroupName In :1", groupnamelist)
        return q          
    
    def GetAllPayouts(self):
        return self.Payouts
    
    def GetAllIncomes(self):
        return self.Incomes
    
    def GetLatestPayoutDesc(self, group, count = 5):
        __typecheck__(group, XMSGroup)
        query = self.SubmitedPayout.filter('Group =', group).order('-Datetime')
        latest = query.fetch(count)
        return set([p.Details for p in latest])
    
    def GetLatestIncomeDesc(self, group, count = 5):
        __typecheck__(group, XMSGroup)
        query = self.SubmitedIncome.filter('Group =', group).order('-Datetime')
        latest = query.fetch(count)
        return set([i.Details for i in latest])
    
    def GetTotalPayoutByGroup(self,group):
        __typecheck__(group, XMSGroup)
        total = 0
        for payout in self.Payouts:
            if payout.Group.key() == group.key():
                total += payout.Money
        return total
    
    def GetTotalIncomeByGroup(self,group):
        __typecheck__(group, XMSGroup)
        total = 0
        for income in self.Incomes:
            if income.Group.key() == group.key():
                total += income.Money
        return total
    
class XMSGroup(db.Model):
    GroupName = db.StringProperty()
    Description = db.StringProperty(multiline=True)
    Actived = db.BooleanProperty()
    Privated = db.BooleanProperty(default=False)
    CreateDate = db.DateTimeProperty(auto_now_add=True)
    Owner = db.ReferenceProperty(reference_class=XMSUser, collection_name="OwnGroups")
    AWaitingJoinUser = db.ListProperty(users.User)
    
    def __cmp__(self, obj):
        return self.key() == obj.key()
    
    @staticmethod
    def CheckGroupExist(groupname):
        for group in XMSGroup.all():
            if group.GroupName.lower() == groupname.lower():
                return True
        return False
    
    @staticmethod
    def GetGroupByName(groupname):
        q = db.GqlQuery("SELECT * FROM XMSGroup Where GroupName=:1", groupname)
        li = q.fetch(1)
        return li and li[0] 
       
    @staticmethod
    def GetActivedGroupByName(groupname, actived = True):
        q = db.GqlQuery("SELECT * FROM XMSGroup Where GroupName=:1 And Actived=:2", groupname, actived)
        li = q.fetch(1)
        return li and li[0] 
    
    @staticmethod
    def GetPublicGroups(actived = True):
        query = XMSGroup.all()
        query.filter('Privated =', False)
        query.filter('Actived =', actived)
        return query
    
    def ApproveAwaitingUser(self, xmsuser):
        __typecheck__(xmsuser, XMSUser)
        if xmsuser.User in self.AWaitingJoinUser:
            if not xmsuser.IsUserOfGroup(self):
                gu = XMSGroupAndUser(User=xmsuser,Group=self )
                gu.put()
            self.AWaitingJoinUser.remove(xmsuser.User)
            self.save()
    
    def DeleteAwaitingUser(self,email):
        for u in self.AWaitingJoinUser:
            if u.email() == email:
                self.AWaitingJoinUser.remove(u)
                self.save()
                return
 
    def SetOwner(self, xmsuser):
        __typecheck__(xmsuser, XMSUser)
        self.Owner = xmsuser
        
    def IsOwner(self, xmsuser):
        __typecheck__(xmsuser, XMSUser)
        return self.Owner.User.email() == xmsuser.User.email()
    
    def DeleteUser(self, xmsuser):
        __typecheck__(xmsuser, XMSUser)
        for gu in self.GroupUsers:
            if gu.User.NickName == xmsuser.NickName:
                db.delete(gu)
                break
    
    def Admin2User(self, xmsuser):
        __typecheck__(xmsuser, XMSUser)
        for gu in self.GroupAdmins:
            if gu.Admin.NickName == xmsuser.NickName:
                db.delete(gu)
                break
        if not xmsuser.IsUserOfGroup(self):
            au = XMSGroupAndUser(User=xmsuser,Group=self )
            au.put()
                    
    def User2Admin(self, xmsuser):
        __typecheck__(xmsuser, XMSUser)
        for gu in self.GroupUsers:
            if gu.User.NickName == xmsuser.NickName:
                db.delete(gu)
                break
        if not xmsuser.IsAdminOfGroup(self):
            au = XMSGroupAndAdmin(Admin=xmsuser,Group=self )
            au.put()
    
    def GetUsersQueryByAuth(self,auth='user'):
        if auth == 'user':
            names = set([gu.User.NickName for gu in self.GroupUsers])
        elif auth == 'admin':
            names = set([gu.Admin.NickName for gu in self.GroupAdmins])
        elif auth == 'owner':
            return self.Owner
        names = list(names)
        if not names:
            names = ["<"]
        q = XMSUser.gql("Where NickName In :1", names)
        return q
    
    def GetUsersListByAuth(self,auth='user'):
        if auth == 'user':
            names = set([gu.User.key() for gu in self.GroupUsers])
        elif auth == 'admin':
            names = set([gu.Admin.key() for gu in self.GroupAdmins])
        elif auth == 'owner':
            names = [self.Owner.key()]
        names = list(names)
        return XMSUser.get(names)
    
    def GetAwaitingQuery(self):
        awaitinglist = self.AWaitingJoinUser
        if not awaitinglist:
            awaitinglist = ["<"]
        q = XMSUser.gql("Where User IN :1",awaitinglist)
        return q
    
    def GetAllUsers(self):
        keys = set([gu.User.key() for gu in self.GroupUsers]\
                    + [gu.Admin.key() for gu in self.GroupAdmins]\
                    + [self.Owner.key()])
        return XMSUser.get(keys) 
    
    def GetAllAdmins(self):
        keys = set([gu.Admin.key() for gu in self.GroupAdmins ] + [self.Owner.key()])
        return XMSUser.get(keys) 
    
    def GetAllPayouts(self):
        return self.Payouts
    
    def GetPayoutsByDate(self, order="desc"):
        q = self.Payouts
        q.order('-Datetime')
        return q
    
    def GetIncomesByDate(self, order="desc"):
        q = self.Incomes
        q.order('-Datetime')
        return q

    def GetPayoutsByUser(self):
        q = self.Payouts
        q.order('User')
        return q
    
    def GetMonthSummary(self, today = datetime.now()):
        this_month = notpayCount = total_notpay = \
        total_income = total_paied = 0
        for payout in self.Payouts:
            if payout.Datetime.year == today.year and \
                payout.Datetime.month == today.month:
                this_month += payout.Money
            if payout.IsPay == True:
                total_paied += payout.Money
            else:
                notpayCount += 1
                total_notpay += payout.Money
                     
        total_income = self.GetTotalIncome()
        
        return (this_month,notpayCount,total_income,total_notpay,total_paied)
    
    def GetTotalNotPayout(self):
        return sum([ payout.Money for payout in self.Payouts if payout.IsPay == False])
    
    def GetTotalPayout(self):
        return sum([ payout.Money for payout in self.Payouts])
    
    def GetTotalIncome(self):
        return sum([ income.Money for income in self.Incomes])
    
    def GetTotalPayoutByUser(self,user):
        __typecheck__(user, XMSUser)
        return sum([ payout.Money for payout in self.Payouts.filter('User=',user)])
    
    def GetTotalIncomeByUser(self,user):
        __typecheck__(user, XMSUser)
        return sum([ income.Money for income in self.Incomes.filter('User=',user)])
       

class XMSGroupAndAdmin(db.Model):
    Admin = db.ReferenceProperty(reference_class=XMSUser, collection_name="AdminGroups")
    Group = db.ReferenceProperty(reference_class=XMSGroup, collection_name="GroupAdmins")
          
class XMSGroupAndUser(db.Model):
    User = db.ReferenceProperty(reference_class=XMSUser, collection_name="UserGroups")
    Group = db.ReferenceProperty(reference_class=XMSGroup, collection_name="GroupUsers")
 
class XMSPayout(db.Model):
    Datetime = db.DateTimeProperty(auto_now_add=True)
    Money = db.FloatProperty()
    Details = db.StringProperty(multiline=True)
    IsPay = db.BooleanProperty(default=False)
    User = db.ReferenceProperty(reference_class=XMSUser, collection_name="Payouts")
    Group = db.ReferenceProperty(required=True, reference_class=XMSGroup, collection_name="Payouts")
    Approver = db.ReferenceProperty(reference_class=XMSUser, collection_name="ApprovedPayout")
    Submiter = db.ReferenceProperty(reference_class=XMSUser, collection_name="SubmitedPayout")
        
class XMSIncome(db.Model):
    Datetime = db.DateTimeProperty(auto_now_add=True)
    Money = db.FloatProperty()
    Details = db.StringProperty(multiline=True)
    User = db.ReferenceProperty(reference_class=XMSUser, collection_name="Incomes")
    Group = db.ReferenceProperty(required=True, reference_class=XMSGroup, collection_name="Incomes")
    Submiter = db.ReferenceProperty(reference_class=XMSUser, collection_name="SubmitedIncome")
    
class XMSChart(db.Model):
    Path = db.StringProperty(multiline=False)
    ContentType = db.StringProperty(multiline=False)
    Data = db.BlobProperty()
    Date = db.DateTimeProperty(auto_now_add=True)
    
    @staticmethod
    def GetChart(path):
        Md5 = hashlib.md5()
        Md5.update(path)
        path = Md5.hexdigest()
        q = XMSChart.gql("WHERE Path = :1", path)
        result = q.fetch(1)
        return result and result[0]
    
    @staticmethod
    def SaveChart(path,contenttype,data):
        Md5 = hashlib.md5()
        Md5.update(path)
        path = Md5.hexdigest()
        chart = XMSChart.get_or_insert("Path:"+path,Path = path)
        chart.ContentType = contenttype
        chart.Data = db.Blob(data)
        chart.put()
        return chart
    
    
    
    
